/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.graph.Model', {
	init: function(canvas, modelConfig) {
		var me = this, model = canvas.paper[modelConfig.type]().attr(modelConfig.attrs),
				config = {
					flowFrom: modelConfig.type === 'path' ? null : [],
					flowTo: modelConfig.type === 'path' ? null : [],
					canvas: canvas, coreX: 0, coreY: 0
				};
		model.id = model.node.raphaelid = canvas.createId(canvas, model.type, modelConfig.modelId);
		if (!model.data('modelType')) {
			var data = modelConfig.data, graph = canvas.locale.canvasPanel.graph[modelConfig.type], modelType;
			if (data) {
				modelType = data.modelType
			} else if (graph) {
				modelType = graph.modelType;
			}
			if (modelType) {
				model.data('modelType', modelType);
			}
		}
		model.data(modelConfig.data);
		Ext.apply(model, config);
		if (!canvas.readOnly) {
			model.drag(me.onDDMove, me.onDDStart, me.onDDEnd);
		}
		var bbox = model.getBBox();
		model.coreX = bbox.cx;
		model.coreY = bbox.cy;
		if (modelConfig.text) {
			var text = canvas.paper.text().attr(modelConfig.text.attrs);
			model.text = text;
			text.model = model;
			var bbox = text.getBBox();
			text.coreX = bbox.cx;
			text.coreY = bbox.cy;
		}
		return model;
	},
	onDDStart: function(x, y, e) {
		var model = this, canvas = model.canvas, selectBox = canvas.selectBox,
				lineX = canvas.lineX, pathX = lineX.attrs.path,
				lineY = canvas.lineY, pathY = lineY.attrs.path;
		lineX.show();
		lineY.show();
		selectBox.oldX = selectBox.attrs.x;
		selectBox.oldY = selectBox.attrs.y;
		pathX[0][1] = pathX[1][1] = -10;
		lineX.attr({path: pathX});
		pathY[0][2] = pathY[1][2] = -10;
		lineY.attr({path: pathY});
	},
	onDDMove: function(dx, dy, x, y, e) {
		var model = this, canvas = model.canvas, selectBox = canvas.selectBox,
				lineX = canvas.lineX, pathX = lineX.attrs.path,
				lineY = canvas.lineY, pathY = lineY.attrs.path,
				newX = selectBox.oldX + dx, newY = selectBox.oldY + dy;
		newX = Raphael.snapTo(5, newX);
		newY = Raphael.snapTo(5, newY);
		selectBox.attr({x: newX, y: newY});
		pathX[0][1] = pathX[1][1] = selectBox.attrs.x + selectBox.attrs.width / 2;
		lineX.attr({path: pathX});
		pathY[0][2] = pathY[1][2] = selectBox.attrs.y + selectBox.attrs.height / 2;
		lineY.attr({path: pathY});
	},
	onDDEnd: function(x, y, e) {
		var model = this, canvas = model.canvas, selectBox = canvas.selectBox;
		var offsetX = selectBox.attrs.x - selectBox.oldX,
				offsetY = selectBox.attrs.y - selectBox.oldY;
		if (offsetX != 0 || offsetY != 0) {
			Ext.each(canvas.selected, function(model) {
				if (model.type === 'circle' || model.type === 'ellipse') {
					model.oldX = model.attrs.cx;
					model.oldY = model.attrs.cy;
					model.attr({cx: model.oldX + offsetX, cy: model.oldY + offsetY});
				} else {
					model.oldX = model.attrs.x;
					model.oldY = model.attrs.y;
					model.attr({x: model.oldX + offsetX, y: model.oldY + offsetY});
				}
				var bbox = model.getBBox();
				model.coreX = bbox.cx;
				model.coreY = bbox.cy;
				if (model.text) {
					model.text.attr({x: model.coreX, y: bbox.y2 + 10});
				}
				Ext.each(model.flowFrom, function(ff) {
					var oldCoreStart = ff.start, path = ff.attrs.path;
					if (ff.flowFrom) {
						oldCoreStart = [ff.flowFrom.coreX, ff.flowFrom.coreY];
						path[0] = ['M', oldCoreStart[0], oldCoreStart[1]];
					}
					if (path.length > 2) {
						if (Ext.Array.contains(canvas.selected, ff.flowFrom)) {
							Ext.each(path, function(point) {
								point[1] += offsetX;
								point[2] += offsetY;
							});
						} else {
							for (var i = 1; i < path.length - 1; i++) {
								if (model.isPointInside(path[i][1], path[i][2])) {
									Ext.Array.remove(path, path[i]);
									i--;
								}
							}
//							if (path[len - 2][1] == path[len - 1][1]) {
//								path[len - 2][1] += offsetX;
//							}
//							if (path[len - 2][2] == path[len - 1][2]) {
//								path[len - 2][2] += offsetY;
//							}
						}
					}
					path[path.length - 1] = ['L', model.coreX, model.coreY];
					ff.attr({path: path});
					canvas.connect(ff);
				});
				Ext.each(model.flowTo, function(ft) {
					var oldCoreEnd = ft.end, path = ft.attrs.path;
					if (ft.flowTo) {
						oldCoreEnd = [ft.flowTo.coreX, ft.flowTo.coreY];
						path[path.length - 1] = ['L', oldCoreEnd[0], oldCoreEnd[1]];
					}
					if (path.length > 2) {
						if (!Ext.Array.contains(canvas.selected, ft.flowTo)) {
							for (var i = 1; i < path.length - 1; i++) {
								if (model.isPointInside(path[i][1], path[i][2])) {
									Ext.Array.remove(path, path[i]);
									i--;
								}
							}
//							if (path[1][1] == path[0][1]) {
//								path[1][1] += offsetX;
//							}
//							if (path[1][2] == path[0][2]) {
//								path[1][2] += offsetY;
//							}
						}
					}
					path[0] = ['M', model.coreX, model.coreY];
					ft.attr({path: path});
					canvas.connect(ft);
				});
			});
		}
		canvas.lineX.hide();
		canvas.lineY.hide();
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.graph.Circle', {
	extend: 'SvgEditor.graph.Model'
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.graph.Ellipse', {
	extend: 'SvgEditor.graph.Model'
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.graph.Rect', {
	extend: 'SvgEditor.graph.Model'
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.graph.Anchor', {
	extend: 'SvgEditor.graph.Circle',
	onDDStart: function(x, y, e) {
		var anchor = this, canvas = anchor.canvas, model = canvas.selected[0],
				lineX = canvas.lineX, pathX = lineX.attrs.path,
				lineY = canvas.lineY, pathY = lineY.attrs.path;
		lineX.show();
		lineY.show();
		anchor.oldX = anchor.attrs.cx;
		anchor.oldY = anchor.attrs.cy;
		pathX[0][1] = pathX[1][1] = -10;
		lineX.attr({path: pathX});
		pathY[0][2] = pathY[1][2] = -10;
		lineY.attr({path: pathY});
		anchor.toBack();
		var flow = model, path = flow.attrs.path, flowFrom = flow.flowFrom, flowTo = flow.flowTo, start, end;
		if (anchor.id === 'startP' || anchor.id === 'endP' || anchor.index === 1 || anchor.index === path.length - 2) {
			if (flowFrom) {
				start = [flowFrom.coreX, flowFrom.coreY];
			} else {
				start = flow.start;
			}
			if (flowTo) {
				end = [flowTo.coreX, flowTo.coreY];
			} else {
				end = flow.end;
			}
			path[0] = ['M', start[0], start[1]];
			path[path.length - 1] = ['L', end[0], end[1]];
			flow.attr({path: path});
			flow.start = start;
			flow.end = end;
		}
	},
	onDDMove: function(dx, dy, x, y, e) {
		var anchor = this, canvas = anchor.canvas, flow = canvas.selected[0], path = flow.attrs.path,
				lineX = canvas.lineX, pathX = lineX.attrs.path,
				lineY = canvas.lineY, pathY = lineY.attrs.path,
				newX = anchor.oldX + dx, newY = anchor.oldY + dy;
		newX = Raphael.snapTo(5, newX);
		newY = Raphael.snapTo(5, newY);
		anchor.attr({cx: newX, cy: newY});
		anchor.coreX = anchor.attrs.cx;
		anchor.coreY = anchor.attrs.cy;
		pathX[0][1] = pathX[1][1] = anchor.attrs.cx;
		lineX.attr({path: pathX});
		pathY[0][2] = pathY[1][2] = anchor.attrs.cy;
		lineY.attr({path: pathY});
		if (anchor.id === 'startP') {
			path[0] = ['M', anchor.coreX, anchor.coreY];
			path[path.length - 1] = ['L', flow.end[0], flow.end[1]];
			flow.attr({path: path});
		} else if (anchor.id === 'endP') {
			path[0] = ['M', flow.start[0], flow.start[1]];
			path[path.length - 1] = ['L', anchor.coreX, anchor.coreY];
			flow.attr({path: path});
		} else {
			path[anchor.index] = ['L', anchor.coreX, anchor.coreY];
			flow.attr({path: path});
		}
	},
	onDDEnd: function(x, y, e) {
		var anchor = this, canvas = anchor.canvas, model = canvas.selected[0];
		if (model.type === 'path') {
			var flow = model, flowPlate = flow.flowPlate, path = flow.attrs.path;
			flowPlate.attr({path: path});
			flowPlate.start = flow.start = [path[0][1], path[0][2]];
			flowPlate.end = flow.end = [path[path.length - 1][1], path[path.length - 1][2]];
			if (anchor.id === 'startP') {
				canvas.connect(flow, canvas.target, null);
			} else if (anchor.id === 'endP') {
				canvas.connect(flow, null, canvas.target);
			} else if (anchor.index === 1 || anchor.index === path.length - 2) {
				canvas.connect(flow);
			}
			canvas.viewAnchor(canvas, flow);
		}
		canvas.lineX.hide();
		canvas.lineY.hide();
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.graph.Image', {
	extend: 'SvgEditor.graph.Rect',
	init: function(canvas, modelConfig) {
		var me = this;
//		, img = canvas.img;
//		img.setSrc(modelConfig.attrs.src);
//		var width = img.getWidth(), height = img.getHeight();
//		modelConfig.attrs.width = width;
//		modelConfig.attrs.height = height;
		var model = me.callParent(arguments);
//		img.setSize(1, 1);
		return model;
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.graph.Path', {
	extend: 'SvgEditor.graph.Model',
	init: function(canvas, modelConfig, noPlate) {
		var me = this, config = {
			start: [0, 0], end: [0, 0]
		};
		if (!modelConfig.data) {
			modelConfig.data = {modelType: 'SequenceFlow'};
		} else if (modelConfig.data && !modelConfig.data.modelType) {
			Ext.apply(modelConfig.data, {modelType: 'SequenceFlow'});
		}
		var flow = me.callParent(arguments);
		Ext.apply(flow, config);
		if (!noPlate) {
			var path = flow.attrs.path, len = path.length, flowPlate = flow.clone().
					attr({'arrow-end': '', 'stroke-width': 16, stroke: 'white'}).toBack()
			flow.flowPlate = flowPlate;
			flowPlate.flow = flow;
			flowPlate.start = flow.start = [path[0][1], path[0][2]];
			flowPlate.end = flow.end = [path[len - 1][1], path[len - 1][2]];
			if (!canvas.readOnly) {
				flowPlate.drag(me.onPlateMove, me.onDDStart, me.onPlateEnd);
			}
		}
		return flow;
	},
	onDDStart: function(x, y, e) {
		var flow = this;
		if (flow.flow) {
			flow = flow.flow;
		}
		if (!(flow.flowFrom || flow.flowTo))
			flow.oldPath = Ext.decode(Ext.encode(flow.attrs.path));
	},
	onDDMove: function(dx, dy, x, y, e) {
		var flow = this;
		if (!(flow.flowFrom || flow.flowTo)) {
			var path = flow.attrs.path;
			for (var i = 0; i < path.length; i++) {
				path[i][1] = this.oldPath[i][1] + dx;
				path[i][2] = this.oldPath[i][2] + dy;
			}
			flow.attr({path: path});
			if (flow.flowPlate) {
				flow.flowPlate.attr({path: path});
			}
		}
	},
	onDDEnd: function(x, y, e) {
		var flow = this;
		if (!(flow.flowFrom || flow.flowTo)) {
			var flowPlate = flow.flowPlate, canvas = flow.canvas, path = flow.attrs.path;
			flow.start = [path[0][1], path[0][2]];
			flow.end = [path[path.length - 1][1], path[path.length - 1][2]];
			if (flowPlate) {
				flowPlate.start = flow.start;
				flowPlate.end = flow.end;
			}
			canvas.viewAnchor(canvas, flow);
		}
	},
	onPlateMove: function(dx, dy, x, y, e) {
		var flowPlate = this, flow = flowPlate.flow;
		if (!(flow.flowFrom || flow.flowTo)) {
			var path = [
				['M', flow.start[0] + dx, flow.start[1] + dy],
				['L', flow.end[0] + dx, flow.end[1] + dy]
			];
			flow.attr({path: path});
			flow.flowPlate.attr({path: path});
		}
	},
	onPlateEnd: function(x, y, e) {
		var flowPlate = this, flow = flowPlate.flow;
		if (!(flow.flowFrom || flow.flowTo)) {
			var canvas = flow.canvas, path = flow.attrs.path;
			flowPlate.start = flow.start = [path[0][1], path[0][2]];
			flowPlate.end = flow.end = [path[path.length - 1][1], path[path.length - 1][2]];
			canvas.viewAnchor(canvas, flow);
		}
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.graph.Text', {
	extend: 'SvgEditor.graph.Model'
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.canvas.Operation', {
	addKnuckle: function(canvas) {
		canvas = canvas || this;
		var flow = canvas.selected[0] && canvas.selected[0].type === 'path' ? canvas.selected[0] : null
		if (flow) {
			var path = flow.attrs.path,
					msg = [canvas.locale.canvasPanel.operation.addKnuckle.msg[0], path.length - 2,
						canvas.locale.canvasPanel.operation.addKnuckle.msg[1], path.length - 2].join(""),
					dialog = Ext.Msg.show({
						title: canvas.locale.canvasPanel.operation.addKnuckle.title,
						msg: msg,
						width: 300,
						buttons: Ext.Msg.OKCANCEL,
						prompt: true,
//				multiline: true,
						fn: function(btn, text, me) {
							if (btn === 'ok' && text && text !== '') {
								var index = parseInt(text);
								if (index > path.length - 2) {
									index = path.length - 2;
								}
								index += 1;
								Ext.Array.insert(path, index, [[]]);
								path[index] = ['L', (path[index - 1][1] + path[index + 1][1]) / 2, (path[index - 1][2] + path[index + 1][2]) / 2];
								flow.attr({path: path});
								canvas.viewAnchor(canvas, flow);
							} else {
								return;
							}
						},
						icon: Ext.MessageBox.INFO
					});
		}
	},
	removeKnuckle: function(canvas, flow) {
		canvas = canvas || this;
		var flow = canvas.selected[0] && canvas.selected[0].type === 'path' ? canvas.selected[0] : null,
				path = flow.attrs.path;
		if (flow && path.length > 2) {
			var msg = [canvas.locale.canvasPanel.operation.removeKnuckle.msg[0], path.length - 2,
				canvas.locale.canvasPanel.operation.removeKnuckle.msg[1], path.length - 3].join(""),
					dialog = Ext.Msg.show({
						title: canvas.locale.canvasPanel.operation.removeKnuckle.title,
						msg: msg,
						width: 300,
						buttons: Ext.Msg.OKCANCEL,
						prompt: true,
//				multiline: true,
						fn: function(btn, text, me) {
							if (btn === 'ok' && text && text !== '') {
								var index = parseInt(text);
								if (index > path.length - 3) {
									index = path.length - 3;
								}
								index += 1;
								Ext.Array.remove(path, path[index]);
								if (flow.flowFrom) {
									path[0] = ['M', flow.flowFrom.coreX, flow.flowFrom.coreY];
								}
								if (flow.flowTo) {
									path[path.length - 1] = ['L', flow.flowTo.coreX, flow.flowTo.coreY];
								}
								flow.attr({path: path});
								canvas.connect(flow);
								canvas.viewAnchor(canvas, flow);
							} else {
								return;
							}
						},
						icon: Ext.MessageBox.INFO
					});
		}
	},
	getModelById: function(canvas, id) {
		return canvas.paper.getById(id);
	},
	setModelId: function(model, id) {
		if (model) {
			model.id = model.node.raphaelid = id;
		}
	},
	setModelAttrs: function(model, attrs) {
		if (model) {
			model.attr(attrs);
			if (model.type === 'circle' || model.type === 'ellipse') {
				model.coreX = model.attrs.cx;
				model.coreY = model.attrs.cy;
			} else if (model.type === 'image' || model.type === 'rect') {
				model.coreX = model.attrs.x + model.attrs.width / 2;
				model.coreY = model.attrs.y + model.attrs.height / 2;
			}
		}
	},
	getFlowFrom: function(model) {
		return model.flowFrom;
	},
	getFlowTo: function(model) {
		return model.flowTo;
	},
	createModel: function(canvas, modelConfig) {
		return canvas.mixins[modelConfig.type].init(canvas, modelConfig);
	},
	addModel: function(canvas, modelConfig, x, y, byDD) {
		canvas = canvas || this;
		if (!modelConfig.data) {
			modelConfig.data = {modelType: modelConfig.id};
		} else if (modelConfig.data && !modelConfig.data.modelType) {
			Ext.apply(modelConfig.data, {modelType: modelConfig.id});
		}
		if (x && y) {
			x = x - canvas.origin.offsetX;
			y = y - canvas.origin.offsetY;
		} else {
			x = 100, y = 50;
		}
		var model;
		if (modelConfig.type === 'path') {
			var path = [
				['M', (x - 50), y],
				['L', (x + 50), y]
			], pathConfig = {
				type: 'path',
				data: modelConfig.data,
				attrs: {
					path: path,
					stroke: 'black',
					'stroke-width': 2,
					'arrow-end': 'classic-wide-long'
				}
			};
			if (modelConfig.attrs) {
				Ext.apply(pathConfig.attrs, modelConfig.attrs);
			}
			model = canvas.createModel(canvas, pathConfig);
		} else if (modelConfig.type === 'circle') {
			var circleConfig = {
				type: 'circle',
				data: modelConfig.data,
				attrs: {
					cx: x, cy: y, r: modelConfig.r || 20, fill: 'white'
				}
			};
			if (modelConfig.attrs) {
				Ext.apply(circleConfig.attrs, modelConfig.attrs);
			}
			model = canvas.createModel(canvas, circleConfig);
		} else if (modelConfig.type === 'ellipse') {
			var ellipseConfig = {
				type: 'ellipse',
				data: modelConfig.data,
				attrs: {
					cx: x, cy: y,
					rx: modelConfig.rx || 50, ry: modelConfig.ry || 40,
					fill: 'white'
				}
			};
			if (modelConfig.attrs) {
				Ext.apply(ellipseConfig.attrs, modelConfig.attrs);
			}
			model = canvas.createModel(canvas, ellipseConfig);
		} else if (modelConfig.view) {
			var imageConfig = {
				type: 'image',
				data: modelConfig.data,
				attrs: {
					src: modelConfig.view,
					x: x, y: y, fill: 'white',
					width: modelConfig.width || 100, height: modelConfig.height || 80
				}
			};
			if (modelConfig.attrs) {
				Ext.apply(imageConfig.attrs, modelConfig.attrs);
			}
			model = canvas.createModel(canvas, imageConfig);
		} else {
			var rectConfig = {
				type: 'rect',
				data: modelConfig.data,
				attrs: {
					x: x, y: y, fill: 'white',
					width: modelConfig.width || 100, height: modelConfig.height || 80
				}
			};
			if (modelConfig.attrs) {
				Ext.apply(rectConfig.attrs, modelConfig.attrs);
			}
			model = canvas.createModel(canvas, rectConfig);
		}
		if (x && y && byDD) {
			x = model.attrs.x - (model.coreX - model.attrs.x);
			y = model.attrs.y - (model.coreY - model.attrs.y);
			canvas.setModelAttrs(model, {x: x, y: y});
		}
		if (byDD && canvas.selected.length === 1 && model.type !== 'path') {
			canvas.link(canvas, canvas.selected[0], model);
		}
		canvas.selectModel(canvas, model);
		return model;
	},
	copy: function(canvas) {
		canvas = canvas || this;
		if (canvas.selected.length > 0) {
			canvas.ClipBoard = canvas.selected;
		}
	},
	paste: function(canvas) {
		canvas = canvas || this;
		var newSelected = [], offsetX = 50, offsetY = 50;
		Ext.each(canvas.ClipBoard, function(model) {
			var newModelConfig = {
				type: model.type,
				data: model.data(),
				attrs: Ext.decode(Ext.encode(model.attrs))
			};
			if (model.text) {
				var textConfig = {
					type: 'text',
					attrs: Ext.decode(Ext.encode(model.text.attrs))
				};
				textConfig.attrs.x += offsetX;
				textConfig.attrs.y += offsetY;
				newModelConfig.text = textConfig;
			}
			if (model.type === 'circle' || model.type === 'ellipse') {
				newModelConfig.attrs.cx += offsetX;
				newModelConfig.attrs.cy += offsetY
			} else if (model.type === 'path') {
				var path = newModelConfig.attrs.path;
				Ext.each(path, function(item) {
					item[1] += offsetX;
					item[2] += offsetY;
				});
			} else {
				newModelConfig.attrs.x += offsetX;
				newModelConfig.attrs.y += offsetY;
			}
			var newModel = canvas.createModel(canvas, newModelConfig);
			newSelected.push(newModel);
		});
		canvas.ClipBoard = canvas.selected = newSelected;
		if (newSelected.length == 1) {
			canvas.selectModel(canvas, newSelected[0]);
		} else {
			var selectBox = canvas.selectBox, x = selectBox.attrs.x, y = selectBox.attrs.y;
			x += offsetX;
			y += offsetY;
			canvas.selectBox.attr({x: x, y: y});
		}
	},
	removeModel: function(canvas) {
		canvas = canvas || this;
		if (canvas.selected.length > 0) {
			Ext.each(canvas.selected, function(model) {
				if (model.text) {
					model.text.remove();
				}
				if (model.flowPlate) {
					model.flowPlate.remove();
				}

				if (Array.isArray(model.flowFrom)) {
					Ext.each(model.flowFrom, function(ff) {
						var src = ff.flowFrom;
						Ext.Array.remove(src.flowTo, ff);
						if (ff.text) {
							ff.text.remove();
						}
						ff.flowPlate.remove();
						ff.remove();
					});
				} else if (model.flowFrom) {
					Ext.Array.remove(model.flowFrom.flowTo, model);
				}
				if (Array.isArray(model.flowTo)) {
					Ext.each(model.flowTo, function(ft) {
						var target = ft.flowTo;
						Ext.Array.remove(target.flowFrom, ft);
						if (ft.text) {
							ft.text.remove();
						}
						ft.flowPlate.remove();
						ft.remove();
					});
				} else if (model.flowTo) {
					Ext.Array.remove(model.flowTo.flowFrom, model);
				}
				model.remove();
			});
			canvas.selected = [];
			canvas.selectModel(canvas);
			canvas.selectBox.hide();
			canvas.widget.hide();
		}
	},
	link: function(canvas, src, target) {
		canvas = canvas || this;
		if (src && target) {
			if (src.type === 'path' || target.type === 'path') {
				return;
			} else {
				var path = [
					['M', src.coreX, src.coreY],
					['L', target.coreX, target.coreY]
				], pathConfig = {
					type: 'path',
					attrs: {
						path: path,
						stroke: 'black',
						'stroke-width': 2,
						'arrow-end': 'classic-wide-long'
					}
				},
				flow = canvas.createModel(canvas, pathConfig);
				canvas.connect(flow, src, target);
			}
		}
	},
	connect: function(flow, src, target) {
		if (src) {
			if (src.id === 'startP') {
				if (flow.flowFrom) {
					Ext.Array.remove(flow.flowFrom.flowTo, flow);
				}
				flow.flowFrom = null;
			} else {
				flow.flowFrom = src;
				src.flowTo.push(flow);
			}
		} else {
			src = flow.flowFrom;
		}
		if (target) {
			if (target.id === 'endP') {
				if (flow.flowTo) {
					Ext.Array.remove(flow.flowTo.flowFrom, flow);
				}
				flow.flowTo = null;
			} else {
				flow.flowTo = target;
				target.flowFrom.push(flow);
			}
		} else {
			target = flow.flowTo;
		}
		var pi = Math.PI, path = flow.attrs.path, start = flow.start, end = flow.end, angle, util = flow.canvas.util;

		if (src) {
			var secondStart = [path[1][1], path[1][2]], srcBox = src.getBBox();
			angle = Raphael.angle(src.coreX, src.coreY, secondStart[0], secondStart[1]) - 180;
			if (src.type === 'circle') {
				var r = src.attrs.r + 2, cos = Math.cos(angle / 180 * pi), sin = Math.sin(angle / 180 * pi);
				var a = r * util.minimum(cos), b = r * util.minimum(sin);
				start = [src.coreX + a, src.coreY + b];
			} else {
				if (angle == -135) {
					start[0] = srcBox.x - 2;
					start[1] = srcBox.y - 2;
				} else if (angle == -45) {
					start[0] = srcBox.x + 2;
					start[1] = srcBox.y - 2;
				} else if (angle == 45) {
					start[0] = srcBox.x + 2;
					start[1] = srcBox.y + 2;
				} else if (angle == 135) {
					start[0] = srcBox.x - 2;
					start[1] = srcBox.y + 2;
				} else if (angle > 135 || angle < -135) {
					start[0] = srcBox.x - 2;
					a = src.coreX - start[0];
					b = Math.ceil(a * util.minimum(Math.tan(angle / 180 * pi)));
					start[1] = src.coreY - b;
				} else if (angle > -135 && angle < -45) {
					angle += 90;
					start[1] = srcBox.y - 2;
					a = src.coreY - start[1];
					b = Math.ceil(a * util.minimum(Math.tan(angle / 180 * pi)));
					start[0] = src.coreX + b;
				} else if (angle > -45 && angle < 45) {
					start[0] = srcBox.x2 + 2;
					a = start[0] - src.coreX;
					b = Math.ceil(a * util.minimum(Math.tan(angle / 180 * pi)));
					start[1] = src.coreY + b;
				} else if (angle > 45 && angle < 135) {
					angle -= 90;
					start[1] = srcBox.y2 + 2;
					a = start[1] - src.coreY;
					b = Math.ceil(a * util.minimum(Math.tan(angle / 180 * pi)));
					start[0] = src.coreX - b;
				}
			}
		}

		if (target) {
			var secondEnd = [path[path.length - 2][1], path[path.length - 2][2]], targetBox = target.getBBox();
			angle = Raphael.angle(target.coreX, target.coreY, secondEnd[0], secondEnd[1]) - 180;
			if (target.type === 'circle') {
				var r = target.attrs.r + 2, cos = Math.cos(angle / 180 * pi), sin = Math.sin(angle / 180 * pi);
				var a = r * util.minimum(cos), b = r * util.minimum(sin);
				end = [target.coreX + a, target.coreY + b];
			} else {
				var a, b;
				if (angle == -135) {
					start[0] = srcBox.x - 2;
					start[1] = srcBox.y - 2;
				} else if (angle == -45) {
					start[0] = srcBox.x + 2;
					start[1] = srcBox.y - 2;
				} else if (angle == 45) {
					start[0] = srcBox.x + 2;
					start[1] = srcBox.y + 2;
				} else if (angle == 135) {
					start[0] = srcBox.x - 2;
					start[1] = srcBox.y + 2;
				} else if (angle > 135 || angle < -135) {
					end[0] = targetBox.x - 2;
					a = target.coreX - end[0];
					b = Math.ceil(a * util.minimum(Math.tan(angle / 180 * pi)));
					end[1] = target.coreY - b;
				} else if (angle > -135 && angle < -45) {
					angle += 90;
					end[1] = targetBox.y - 2;
					a = target.coreY - end[1];
					b = Math.ceil(a * util.minimum(Math.tan(angle / 180 * pi)));
					end[0] = target.coreX + b;
				} else if (angle > -45 && angle < 45) {
					end[0] = targetBox.x2 + 2;
					a = end[0] - target.coreX;
					b = Math.ceil(a * util.minimum(Math.tan(angle / 180 * pi)));
					end[1] = target.coreY + b;
				} else if (angle > 45 && angle < 135) {
					angle -= 90;
					end[1] = targetBox.y2 + 2;
					a = end[1] - target.coreY;
					b = Math.ceil(a * util.minimum(Math.tan(angle / 180 * pi)));
					end[0] = target.coreX - b;
				}
			}
		}
		var flowPlate = flow.flowPlate;
		path[0] = ['M', start[0], start[1]];
		path[path.length - 1] = ['L', end[0], end[1]];
		flow.attr({path: path});
		flowPlate.attr({path: path});
		flowPlate.start = flow.start = start;
		flowPlate.end = flow.end = end;
	},
	setDiagram: function(canvas, diagram) {
		canvas = canvas || this;
		Ext.each(diagram, function(modelConfig) {
			canvas.createModel(canvas, modelConfig);
		});
		Ext.each(diagram, function(modelConfig) {
			var model = canvas.paper.getById(modelConfig.modelId);
			if (Array.isArray(modelConfig.flowFromId)) {
				Ext.each(modelConfig.flowFromId, function(ffi) {
					var flowFrom = canvas.paper.getById(ffi);
					model.flowFrom.push(flowFrom);
				});
			} else {
				model.flowFrom = canvas.paper.getById(modelConfig.flowFromId);
			}

			if (Array.isArray(modelConfig.flowToId)) {
				Ext.each(modelConfig.flowToId, function(fti) {
					var flowTo = canvas.paper.getById(fti);
					model.flowTo.push(flowTo);
				});
			} else {
				model.flowTo = canvas.paper.getById(modelConfig.flowToId);
			}
		});
	},
	getDiagram: function(canvas) {
		canvas = canvas || this;
		var ms = canvas.modelSet;
		if (!ms) {
			ms = canvas.modelSet = canvas.paper.setFinish();
		}
		var diagram = [];
		Ext.each(ms.items, function(model) {
			if (model.type !== 'text' && !model.flow) {
				var modelData = model.data();
				var modelConfig = {
					modelId: model.id, type: model.type,
					attrs: model.attrs,
					data: modelData
				};
				if (modelData.text) {
					modelConfig.text = {
						type: 'text',
						attrs: model.text.attrs
					};
				}
				if (Array.isArray(model.flowFrom)) {
					modelConfig.flowFromId = [];
					Ext.each(model.flowFrom, function(ff) {
						modelConfig.flowFromId.push(ff.id);
					});
				} else if (model.flowFrom) {
					modelConfig.flowFromId = model.flowFrom.id;
				}
				if (Array.isArray(model.flowTo)) {
					modelConfig.flowToId = [];
					Ext.each(model.flowTo, function(ft) {
						modelConfig.flowToId.push(ft.id);
					});
				} else if (model.flowTo) {
					modelConfig.flowToId = model.flowTo.id;
				}
				diagram.push(modelConfig);
			}
		});
		canvas.paper.setStart(ms);
		return diagram;
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.canvas.Widget', {
	extend: 'Ext.container.Container',
	canvas: null,
	width: 46,
	defaults: {
		columnWidth: .5
	},
	initComponent: function() {
		var me = this;
		me.initQuickSvg(me);
		me.items = me.quickSvg;
		me.callParent();
	},
	initQuickSvg: function(me) {
		me.quickSvg = [
			Ext.create('Ext.button.Button', {
				name: 'UserTask',
				iconCls: 'User',
//				text: '圆形',
				handler: me.newModel,
				canvas: me.canvas
			}),
			Ext.create('Ext.button.Button', {
				name: 'circle',
				iconCls: 'Circle',
//				text: '圆形',
				handler: me.newModel,
				canvas: me.canvas
			}),
			Ext.create('Ext.button.Button', {
				name: 'rect',
				iconCls: 'Rect',
//				text: '矩形',
				handler: me.newModel,
				canvas: me.canvas
			}),
			Ext.create('Ext.button.Button', {
				name: 'ellipse',
				iconCls: 'Ellipse',
//				text: '椭圆',
				handler: me.newModel,
				canvas: me.canvas
			}),
			Ext.create('Ext.button.Button', {
				name: 'path',
				iconCls: 'Path',
//				text: '箭头',
				handler: me.newModel,
				canvas: me.canvas
			})
		];
	},
	newModel: function() {
		var type = this.name, canvas = this.canvas, origin = canvas.origin, modelConfig,
				node = canvas.treeStore.getNodeById(type);
		if (node) {
			modelConfig = node.data;
		} else {
			modelConfig = {
				type: type
			};
		}
		if (type === 'image') {
			modelConfig.view = this.view;
		}
		if (this.attrs) {
			modelConfig.attrs = this.attrs;
		}
		if (canvas.selected.length === 1 && type !== 'path') {
			var selectedModel = canvas.selected[0], x, y;
			if (type === 'circle' || type === 'ellipse') {
				x = selectedModel.coreX + 150;
				y = selectedModel.coreY + origin.offsetY;
			} else {
				x = selectedModel.coreX + 150;
				y = selectedModel.coreY + origin.offsetY - 40;
			}
			x += origin.offsetX;
			var newModel = canvas.addModel(canvas, modelConfig, x, y);
			canvas.link(canvas, selectedModel, newModel);
		} else if (type !== 'path') {
			canvas.addModel(canvas, modelConfig);
		}
	},
	afterFirstLayout: function() {
		var me = this, canvas = me.canvas;
		me.callParent();
		new Ext.dd.DragSource(me.items.items[4].getId(), {
			group: 'pathDD',
			afterDragDrop: function(target, e, id) {
				var canvas = Ext.getCmp(id);
				canvas.link(canvas, canvas.selected[0], canvas.target);
			}
		});
		var target = new Ext.dd.DDTarget(canvas.getId(), 'pathDD');
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.canvas.Quickbar', {
	extend: 'Ext.toolbar.Toolbar',
	canvas: null,
	initComponent: function() {
		var me = this;
		me.initQuickBtn(me);
		me.items = me.quickBtn;
		me.callParent();
	},
	initQuickBtn: function(me) {
		me.copyBtn = Ext.create('Ext.button.Button', {
			name: 'copy',
			text: me.locale.canvasPanel.quickbar.copyBtn.text,
			tooltip: me.locale.canvasPanel.quickbar.copyBtn.qtip,
			canvas: me.canvas,
			handler: me.doOperation
		});
		me.pasteBtn = Ext.create('Ext.button.Button', {
			name: 'paste',
			text: me.locale.canvasPanel.quickbar.pasteBtn.text,
			tooltip: me.locale.canvasPanel.quickbar.pasteBtn.qtip,
			canvas: me.canvas,
			handler: me.doOperation
		});
		me.removeModelBtn = Ext.create('Ext.button.Button', {
			name: 'removeModel',
			text: me.locale.canvasPanel.quickbar.removeModelBtn.text,
			tooltip: me.locale.canvasPanel.quickbar.removeModelBtn.qtip,
			canvas: me.canvas,
			handler: me.doOperation
		});
		me.addKnuckleBtn = Ext.create('Ext.button.Button', {
			name: 'addKnuckle',
			text: me.locale.canvasPanel.quickbar.addKnuckleBtn.text,
			tooltip: me.locale.canvasPanel.quickbar.addKnuckleBtn.qtip,
			canvas: me.canvas,
			handler: me.doOperation
		});
		me.removeKnuckleBtn = Ext.create('Ext.button.Button', {
			name: 'removeKnuckle',
			text: me.locale.canvasPanel.quickbar.removeKnuckleBtn.text,
			tooltip: me.locale.canvasPanel.quickbar.removeKnuckleBtn.qtip,
			canvas: me.canvas,
			handler: me.doOperation
		});
		me.quickBtn = [me.copyBtn, me.pasteBtn, me.removeModelBtn, me.addKnuckleBtn, me.removeKnuckleBtn];
	},
	doOperation: function() {
		var operation = this.name, canvas = this.canvas;
		canvas[operation](canvas);
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.canvas.SelectBox', {
	selecteBox: null,
	lineX: null,
	lineY: null,
	initLineXY: function(canvas) {
		var canvas = canvas || this, lineXConfig = {
			modelId: 'lineX',
			type: 'path',
			attrs: {
				path: [['M', 0, 0], ['L', 0, canvas.paper.height]],
				fill: 'none', stroke: 'green', 'stroke-width': 1,
				'stroke-linecap': 'round',
				'stroke-linejoin': 'round',
				'stroke-dasharray': "-"
			}
		}, lineYConfig = {
			modelId: 'lineX',
			type: 'path',
			attrs: {
				path: [['M', 0, 0], ['L', canvas.paper.width, 0]],
				fill: 'none', stroke: 'green', 'stroke-width': 1,
				'stroke-linecap': 'round',
				'stroke-linejoin': 'round',
				'stroke-dasharray': "-"
			}
		};
		canvas.lineX = canvas.mixins[lineXConfig.type].init(canvas, lineXConfig, true).hide();
		canvas.lineY = canvas.mixins[lineXConfig.type].init(canvas, lineYConfig, true).hide();
	},
	initSelectBox: function(canvas) {
		var selectBoxConfig = {
			modelId: 'selectBox',
			type: 'rect',
			attrs: {
				fill: 'none', stroke: 'blue', 'stroke-width': 1,
				'stroke-linecap': 'round',
				'stroke-linejoin': 'round',
				'stroke-dasharray': "-"
			}
		};
		canvas.selectBox = canvas.createModel(canvas, selectBoxConfig).hide();
	},
	setAnchor: function(canvas, selectBox, xy) {
		selectBox.anchorX = xy[0], selectBox.anchorY = xy[1];
		selectBox.attr({x: xy[0], y: xy[1], width: 0, height: 0}).show().toFront();
		selectBox.view = true;
	},
	changeSelectBox: function(canvas, selectBox, xy) {
		var x, y, width, height;
		if (selectBox.anchorX < xy[0]) {
			x = selectBox.anchorX;
			width = xy[0] - x;
		} else {
			x = xy[0];
			width = selectBox.anchorX - x;
		}
		if (selectBox.anchorY < xy[1]) {
			y = selectBox.anchorY;
			height = xy[1] - y;
		} else {
			y = xy[1];
			height = selectBox.anchorY - y;
		}
		selectBox.attr({
			x: x - canvas.origin.offsetX, y: y - canvas.origin.offsetY,
			width: width, height: height
		});
	},
	selectModel: function(canvas, model) {
		var selectBox = canvas.selectBox, bbox, propertiesPanel = canvas.propertiesPanel,
				minX = canvas.getWidth(), minY = canvas.getHeight(), maxX = 0, maxY = 0;
		canvas.focus();
		canvas.selected = [];
		if (propertiesPanel) {
			propertiesPanel.initAttributes(propertiesPanel);
		}
		canvas.viewAnchor(canvas, null);
		canvas.widget.hide();
		if (model) {
			canvas.selected = [model];
			if (model.type === 'path') {
				canvas.viewAnchor(canvas, model);
			} else {
				bbox = model.getBBox(), minX = bbox.x, minY = bbox.y, maxX = bbox.x2, maxY = bbox.y2;
				selectBox.attr({x: minX, y: minY, width: maxX - minX, height: maxY - minY}).show();
				selectBox.view = true;
				canvas.widget.show();
				canvas.widget.setXY([
					bbox.x2 + canvas.origin.offsetX + 2 - canvas.body.dom.scrollLeft,
					bbox.y + canvas.origin.offsetY - canvas.body.dom.scrollTop
				]);
			}
			if (propertiesPanel) {
				var modelData = model.data();
				propertiesPanel.setAttributes(propertiesPanel, modelData.modelType);
			}
		} else if (canvas.selected.length == 0) {
			var ms = canvas.modelSet = canvas.paper.setFinish();
			Ext.each(ms, function(model) {
				if (model.type !== 'path' && model.type !== 'text'
						&& canvas.selectBox.isPointInside(model.coreX, model.coreY)) {
					canvas.selected.push(model);
					bbox = model.getBBox();
					minX = minX < bbox.x ? minX : bbox.x;
					minY = minY < bbox.y ? minY : bbox.y;
					maxX = maxX > bbox.x2 ? maxX : bbox.x2;
					maxY = maxY > bbox.y2 ? maxY : bbox.y2;
				}
			});
			if (canvas.selected.length > 0) {
				canvas.selectBox.attr({x: minX, y: minY, width: maxX - minX, height: maxY - minY});
			} else {
				canvas.selectBox.hide();
				canvas.selectBox.view = false;
			}
			canvas.paper.setStart(ms);
		}
		selectBox.oldX = selectBox.attrs.x;
		selectBox.oldY = selectBox.attrs.y;
	},
	selectAll: function(canvas) {
		var ms = canvas.modelSet = canvas.paper.setFinish(), bbox,
				minX = canvas.getWidth(), minY = canvas.getHeight(), maxX = 0, maxY = 0;
		canvas.selected = [];
		Ext.each(ms, function(model) {
			if (model.type !== 'path' && model.type !== 'text') {
				canvas.selected.push(model);
				bbox = model.getBBox();
				minX = minX < bbox.x ? minX : bbox.x;
				minY = minY < bbox.y ? minY : bbox.y;
				maxX = maxX > bbox.x2 ? maxX : bbox.x2;
				maxY = maxY > bbox.y2 ? maxY : bbox.y2;
			}
		});
		canvas.selectBox.attr({x: minX, y: minY, width: maxX - minX, height: maxY - minY}).show();
		canvas.selectBox.view = true;
		canvas.paper.setStart(ms);
		canvas.selectBox.oldX = canvas.selectBox.attrs.x;
		canvas.selectBox.oldY = canvas.selectBox.attrs.y;
	},
	viewAnchor: function(me, model) {
		var anchor = me.anchor;
		anchor.startP.hide();
		anchor.startP.toFront();
		anchor.endP.hide();
		anchor.endP.toFront();
		Ext.each(anchor.knuckles, function(knuckle) {
			knuckle.hide();
			knuckle.toFront();
		});
		if (!model) {
			return;
		} else if (model.type === 'path') {
			var flow = model;
			me.selectBox.hide();
			me.selectBox.view = false;
			var ff = flow.flowFrom, ft = flow.flowTo,
					path = flow.attrs.path, len = path.length,
					start = flow.start, end = flow.end;
			if (ff) {
				anchor.startP.attr({
					cx: ff.coreX, cy: ff.coreY, fill: 'red', 'fill-opacity': 1
				}).show();
			} else {
				anchor.startP.attr({cx: start[0], cy: start[1], fill: 'white'}).show();
			}
			if (ft) {
				anchor.endP.attr({
					cx: ft.coreX, cy: ft.coreY, fill: 'red', 'fill-opacity': 1
				}).show();
			} else {
				anchor.endP.attr({cx: end[0], cy: end[1], fill: 'white'}).show();
			}
			if (len > 2) {
				var canvas = flow.canvas;
				canvas.modelSet = canvas.paper.setFinish();
				for (var i = 1; i < len - 1; i++) {
					var anchorConfig = {
						modelId: 'knuckle' + i,
						type: 'circle',
						attrs: {
							cx: path[i][1], cy: path[i][2],
							r: 3, fill: '#00ff33', 'fill-opacity': 1
						}
					};
					if (anchor.knuckles[i - 1]) {
						anchor.knuckles[i - 1].attr({cx: path[i][1], cy: path[i][2]}).show().toFront();
					} else {
						var knuckle = canvas.mixins.anchor.init(canvas, anchorConfig).toFront();
						anchor.knuckles.push(knuckle);
					}
					anchor.knuckles[i - 1].index = i;
				}
				canvas.paper.setStart(canvas.modelSet);
			}
		}
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.canvas.KeyMap', {
	//ctrl+c
	'ctrl+c': function(canvas) {
		canvas.copy(canvas);
	},
	//ctrl+v
	'ctrl+v': function(canvas) {
		canvas.paste(canvas);
	},
	//Delete
	'del': function(canvas) {
		canvas.removeModel(canvas);
	},
	'ctrl+a': function(canvas) {
		canvas.selectAll(canvas);
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.module.CanvasPanel', {
	extend: 'Ext.panel.Panel',
	mixins: {
		image: 'SvgEditor.graph.Image',
		circle: 'SvgEditor.graph.Circle',
		ellipse: 'SvgEditor.graph.Ellipse',
		path: 'SvgEditor.graph.Path',
		rect: 'SvgEditor.graph.Rect',
		anchor: 'SvgEditor.graph.Anchor',
		text: 'SvgEditor.graph.Text',
		selectBox: 'SvgEditor.canvas.SelectBox',
		keyMap: 'SvgEditor.canvas.KeyMap',
		operation: 'SvgEditor.canvas.Operation'
	},
	autoScroll: true,
	layout: 'absolute',
	locale: null,
	util: null,
	treeStore: null,
	paper: null,
	quickbar: null,
	readOnly: false,
	nowId: 0,
	modelIds: [],
//	img: null,
	enableDragDrop: true,
	origin: {offsetX: 0, offsetY: 0},
	anchor: {startP: null, endP: null, knuckles: []},
	selected: [],
	takeUp: null,
	target: null,
	ClipBoard: null,
	modelSet: null,
	initComponent: function() {
		var me = this;
		if (!me.readOnly) {
			me.initTextView(me);
//		me.img = Ext.create('Ext.Img');
			me.quickbar = me.tbar = Ext.create('SvgEditor.canvas.Quickbar', {canvas: me, locale: me.locale});
			me.widget = Ext.create('SvgEditor.canvas.Widget', {hidden: true, canvas: me});
			me.items = [me.textView, me.widget];//, me.img];
		}
		me.callParent();
	},
	initTextView: function(me) {
		me.textView = Ext.create('Ext.form.field.TextArea', {
			hidden: true,
			thisModel: null,
			width: 100,
			rows: 3,
			focused: false,
			setText: function(model) {
				var text = this.getValue();
				if (model.text && this.isDirty()) {
					model.text.attr({text: text});
				} else {
					var bbox = model.getBBox(), textConfig = {
						type: 'text',
						attrs: {
							x: model.coreX, y: bbox.y2 + 10,
							text: text, font: "14px 宋体"
						}
					};
					model.text = me.createModel(me, textConfig);
					model.text.model = model;
				}
				model.data('text', text);
			},
			getText: function(model) {
				var text = model.data('text');
				this.setValue(text);
			},
			listeners: {
				blur: {
					fn: function() {
						this.setText(this.thisModel);
						this.thisModel = null;
						this.reset();
						this.hide();
						this.focused = false;
					}
				},
				focus: {
					fn: function() {
						this.focused = true;
						this.thisModel = me.selected[0];
						this.getText(this.thisModel);
						me.widget.setX(me.selectBox.attrs.x + me.origin.offsetX - me.widget.getWidth());
					}
				}
			}
		});
	},
	afterFirstLayout: function() {
		var me = this;
		me.initCanvas(me);
		me.layout.outerCt.setHeight(0);
	},
	initCanvas: function(me) {
		if (me.paper) {
			me.modelSet = me.paper.setFinish();
			Ext.each(me.modelSet, function(model) {
				model.removeData();
				if (model.text) {
					model.text.remove();
				}
				if (model.flowPlate) {
					model.flowPlate.remove();
				}
				model.remove();
			});
			me.anchor.startP.remove();
			me.anchor.endP.remove();
			Ext.each(me.anchor.knuckles, function(knuckle) {
				knuckle.remove();
			});
			me.anchor.knuckles = [];
			if (me.selectBox) {
				me.selectBox.remove();
			}
			if (me.lineX && me.lineY) {
				me.lineX.remove();
				me.lineY.remove();
			}
			me.paper.remove();
		}
		me.nowId = 0;
		me.modelIds = [];
//		me.paper = Raphael(me.body.id, me.getWidth(), me.getHeight());
		var width = me.getWidth(), height = me.getHeight;
		width = width > 1000 ? width : 1000;
		height = height > 1000 ? height : 1000;
		me.paper = Raphael(me.body.id, width, height);
		me.paper.renderfix();
		me.origin.offsetX = me.getX();
		me.origin.offsetY = me.getY();
		if (me.quickbar) {
			me.origin.offsetY += me.quickbar.getHeight();
		}
		var anchorConfig = {
			modelId: null,
			type: 'circle',
			attrs: {
				r: 3, fill: 'white', 'fill-opacity': 0
			}
		};
		anchorConfig.modelId = 'startP';
		me.anchor.startP = me.mixins.anchor.init(me, anchorConfig).hide();
		anchorConfig.modelId = 'endP';
		me.anchor.endP = me.mixins.anchor.init(me, anchorConfig).hide();
		me.initSelectBox(me);
		me.initLineXY(me);
		me.widget.hide();
		me.selected = [];
		me.target = null;
		me.modelSet = null;
		me.start = null;
		me.end = null;
		me.paper.setStart();
	},
	createId: function(canvas, type, id) {
		var me = canvas || this;
		if (!id) {
			id = type + '-' + (++me.nowId);
		}
		while (Ext.Array.contains(me.modelIds, id)) {
			id = type + '-' + (++me.nowId);
		}
		me.modelIds.push(id);
		return id;
	},
	getModel: function(canvas, e, t, xy) {
		var model, canvas = canvas || this;
		if (t.raphael) {
			model = canvas.paper.getById(t.raphaelid);
		} else {
			xy = xy ? xy : e.getXY();
			model = canvas.paper.getElementByPoint(xy[0], xy[1])
		}
		if (model && model.type === 'text' && model.model) {
			model = model.model;
		} else if (model && model.type === 'path' && model.flow) {
			model = model.flow;
		}
		return model;
	},
	setData: function(model, key, value) {
		if (model && key && value) {
			model.data(key, value);
			return true;
		} else if (model && key && (typeof key === 'object')) {
			model.data(key);
			return true;
		} else {
			return false;
		}
	},
	getData: function(model, key) {
		if (model && key) {
			return model.data(key);
		} else if (model) {
			return model.data();
		} else {
			return null;
		}
	},
	removeData: function(model, key) {
		if (model && key) {
			model.removeData(key);
			return true;
		} else if (model) {
			model.removeData();
			return true;
		} else {
			return false;
		}
	},
	afterRender: function() {
		var me = this;
		me.callParent();
		if (!me.readOnly) {
			me.el.on('dblclick', me.onDblClick, me);
			me.el.on('mousedown', me.onMousedown, me);
			me.el.on('mousemove', me.onMousemove, me);
			me.el.on('mouseup', me.onMouseup, me);
			me.initKeyMap(me);
		}
	},
	onDblClick: function(e, t) {
		var me = this, textView = me.textView, xy = e.getXY(), model;
		xy[0] += me.body.dom.scrollLeft;
		xy[1] += me.body.dom.scrollTop;
		if (model = me.getModel(me, e, t)) {
			me.selectModel(me, model);
			textView.show();
			textView.setXY(xy);
			textView.focus();
		}
	},
	onMousedown: function(e, t) {
		var me = this, xy = e.getXY(), model = me.getModel(me, e, t), cmp = me.getChildByElement(t),
				dom = me.body.dom;
		if (
				(dom.clientWidth >= dom.offsetWidth - 2 || (dom.clientWidth < dom.offsetWidth - 2 && xy[0] < me.getX() + me.getWidth() - 20))
				&& (dom.clientHeight >= dom.offsetHeight - 2 || (dom.clientHeight < dom.offsetHeight - 2 && xy[1] < me.getY() + me.getHeight() - 20))
				) {
			xy[0] += me.body.dom.scrollLeft;
			xy[1] += me.body.dom.scrollTop;
			if (!(cmp === me.textView || cmp === me.quickbar || cmp === me.widget)) {
				if (model && (me.anchor.hasOwnProperty(model.id) || Ext.Array.contains(me.anchor.knuckles, model))) {
					return;
				} else {
					if (!Ext.Array.contains(me.selected, model)) {
						me.selectModel(me, model);
					}
					if (!model) {
						me.setAnchor(me, me.selectBox, xy);
					} else {
						var selectBox = me.selectBox;
						me.takeUp = model;
						if (!selectBox.view) {
							me.selectModel(me, model);
						}
					}
					me.mousedown = true;
				}
			}
		}
	},
	onMousemove: function(e, t) {
		var me = this;
		if (me.mousedown && me.selectBox.view && !me.takeUp) {
			var xy = e.getXY();
			xy[0] += me.body.dom.scrollLeft;
			xy[1] += me.body.dom.scrollTop;
			me.changeSelectBox(me, me.selectBox, xy);
			me.mousemove = true;
		}
	},
	onMouseup: function(e, t) {
		var me = this, cmp = me.getChildByElement(t), dom = me.body.dom, xy = e.getXY();
		if (
				(dom.clientWidth >= dom.offsetWidth - 2 || (dom.clientWidth < dom.offsetWidth - 2 && xy[0] < me.getX() + me.getWidth() - 20))
				&& (dom.clientHeight >= dom.offsetHeight - 2 || (dom.clientHeight < dom.offsetHeight - 2 && xy[1] < me.getY() + me.getHeight() - 20))
				) {
			me.target = null;
			if (me.selected.length == 1 && me.selected[0].type !== 'path') {
				var bbox = me.selectBox.getBBox();
				me.widget.show();
				me.widget.setXY([
					bbox.x2 + me.origin.offsetX + 2 - me.body.dom.scrollLeft,
					bbox.y + me.origin.offsetY - me.body.dom.scrollTop
				]);
			}
			if (!(me.textView.focused || cmp === me.textView || cmp === me.quickbar || cmp === me.widget)) {
				if (me.selected.length == 1 && t.raphael && me.selected[0].id !== t.raphaelid) {
					me.target = me.getModel(me, e, t);
				}
				if (me.selectBox.view && !me.takeUp && !me.target) {
					me.selectModel(me);
				}
			}
			me.takeUp = null;
			me.mousemove = false;
			me.mousedown = false;
		}
	},
	initKeyMap: function(me) {
		Ext.create('Ext.util.KeyMap', {
			target: me.getId(),
			ctrl: true,
			key: Ext.EventObject.C,
			canvas: me,
			fn: function(key, ev) {
				this.canvas['ctrl+c'](this.canvas);
				ev.stopEvent();
				return false;
			}
		});
		Ext.create('Ext.util.KeyMap', {
			target: me.getId(),
			ctrl: true,
			key: Ext.EventObject.V,
			canvas: me,
			fn: function(key, ev) {
				this.canvas['ctrl+v'](this.canvas);
				ev.stopEvent();
				return false;
			}
		});
		Ext.create('Ext.util.KeyMap', {
			target: me.getId(),
			key: Ext.EventObject.DELETE,
			canvas: me,
			fn: function(key, ev) {
				this.canvas['del'](this.canvas);
				ev.stopEvent();
				return false;
			}
		});
		Ext.create('Ext.util.KeyMap', {
			target: me.getId(),
			ctrl: true,
			key: Ext.EventObject.A,
			canvas: me,
			fn: function(key, ev) {
				this.canvas['ctrl+a'](this.canvas);
				ev.stopEvent();
				return false;
			}
		});
	}
});Ext.define('SvgEditor.property.Property', {
	extend: 'Ext.data.Model',
	fields: [
		{
			name: 'group', // for grouping
			type: 'string'
		},
		{
			name: 'name',
			type: 'string'
		},
		{
			name: 'value'
		},
		{
			name: 'icons'
		},
		{
			name: 'gridProperties'
		}
	]
});/**
 * @class Ext.ux.grid.property.Store
 * @extends Ext.grid.property.Store
 *
 * @author Harald Hanek (c) 2011-2012
 * @license http://harrydeluxe.mit-license.org
 */
/**
 * transform by Ext.ux.grid.property.Grid for loadPath in SvgEditor
 */
Ext.define('SvgEditor.property.Store', {
	extend: 'Ext.grid.property.Store',
	uses: ['SvgEditor.property.Property'],
	proxy: {
		type: 'memory',
		reader: {
			type: 'json'
		}
	},
	/**
	 * Creates new property store.
	 * @param {Ext.grid.Panel} grid The grid this store will be bound to
	 * @param {Object} source The source data config object
	 */
	constructor: function(grid, source)
	{
		var me = this, tmp = me.superclass.constructor; // stores temporary the parent constructor
		me.grid = grid;
		me.source = source;
		delete me.superclass.constructor;
		me.callParent([{
				data: source || [],
				model: SvgEditor.property.Property,
				proxy: me.getProxy(),
				groupField: grid.groupField,
				groupDir: grid.groupDir,
			}]);
		me.superclass.constructor = tmp;
	},
	setValue: function(prop, value, create)
	{
		var me = this, rec = me.getRec(prop);
		if (rec)
		{
			rec.set('value', value);
			me.source[prop] = value;
		}
		else if (create)
		{
			// only create if specified.
			me.source[prop] = value;
			rec = new SvgEditor.property.Property({
				name: prop,
				value: value
			}, prop);
			me.add(rec);
		}
	}
});/**
 * @class Ext.ux.grid.property.HeaderContainer
 * @extends Ext.grid.header.Container
 *
 * A custom HeaderContainer for the {@link Ext.grid.property.Grid}. Generally
 * it should not need to be used directly.
 *
 * @author Harald Hanek (c) 2011-2012
 * @license http://harrydeluxe.mit-license.org
 */
/**
 * transform by Ext.ux.grid.property.Grid for loadPath in SvgEditor
 */
Ext.define('SvgEditor.property.HeaderContainer', {
	extend: 'Ext.grid.header.Container',
	nameWidth: 115,
	// private - strings used for locale support
	nameText: 'Name',
	valueText: 'Value',
	dateFormat: 'm/j/Y',
	trueText: 'true',
	falseText: 'false',
	// private
	nameColumnCls: Ext.baseCSSPrefix + 'grid-property-name',
	/**
	 * Creates new HeaderContainer.
	 *
	 * @param {Ext.grid.property.Grid} grid The grid this store will be bound to
	 * @param {Object} source The source data config object
	 */
	constructor: function(grid, store)
	{
		var me = this, columns = [{
				header: 'property',
				width: grid.nameColumnWidth || me.nameWidth,
				//sortable: grid.sortableColumns,
				sortable: false,
				dataIndex: grid.nameField,
				//renderer: Ext.Function.bind(me.renderProp, me),
				itemId: grid.nameField,
				menuDisabled: true,
				groupField: true,
				tdCls: me.nameColumnCls,
				//wcc
				renderer: grid.tooltipRenderer
			},
			{
				header: 'value',
				//renderer: Ext.Function.bind(me.renderCell, me),
				getEditor: Ext.Function.bind(me.getCellEditor, me),
				//sortable: grid.sortableColumns,
				sortable: false,
				flex: 1,
				fixed: true,
				dataIndex: grid.valueField,
				itemId: grid.valueField,
				menuDisabled: true,
				//wcc
				renderer: grid.valueRenderer
			},
			{
				header: grid.groupField,
				hidden: true,
				//sortable: grid.sortableColumns,
				dataIndex: grid.groupField,
				itemId: grid.groupField
			}];

		if (grid.columns)
			columns = columns.concat(Ext.isArray(grid.columns) ? grid.columns : [grid.columns]);

		me.grid = grid;
		me.store = store;
		me.callParent([{
				/* 4.2.1必须要有isRootHeader=true*/
				isRootHeader: true,
				items: columns
			}]);
	},
	getCellEditor: function(record)
	{
		return this.grid.getCellEditor(record, this);
	},
	/**
	 * Render a property name cell
	 *
	 * @private
	 */
	renderProp: function(v, metadata, record)
	{
		//grid.tooltipRenderer(v, metadata, record);
		return record.data['name'] || this.getPropertyName(v);
	},
	/**
	 * Render a property value cell
	 *
	 * @private
	 */
	renderCell: function(val, meta, rec)
	{
		var me = this,
				renderer = me.grid.customRenderers[rec.get(me.grid.nameField)],
				result = val,
				c = rec.data.renderer != '' ? rec.data.renderer : null;

		renderer = me.grid.customRenderers[c] || c || me.grid.customRenderers[rec.get(me.grid.nameField)]; // harry

		var v = me.grid.customEditors[rec.get('editor')];
		if (!rec.get('renderer') && v)
		{
			var t = v.field || v;
			if (t.rendered && t.rawValue)
				return t.rawValue;
		}

		if (renderer)
		{
			return renderer.apply(me, arguments);
		}

		if (Ext.isDate(val))
		{
			result = me.renderDate(val);
		}
		else if (Ext.isBoolean(val))
		{
			result = me.renderBool(val);
		}
		return Ext.util.Format.htmlEncode(result);
	},
	/**
	 * @private
	 */
	renderDate: Ext.util.Format.date,
	/**
	 * @private
	 */
	renderBool: function(bVal)
	{
		return this[bVal ? 'trueText' : 'falseText'];
	},
	/**
	 * Renders custom property names instead of raw names if defined in the Grid
	 *
	 * @private
	 */
	getPropertyName: function(name)
	{
		var pn = this.grid.propertyNames;
		return pn && pn[name] ? pn[name] : name;
	}
});/**
 * @class Ext.ux.grid.property.Grid
 * @extends Ext.grid.property.Grid
 *
 * @author Harald Hanek (c) 2011-2012
 * @license http://harrydeluxe.mit-license.org
 */
/**
 * transform by Ext.ux.grid.property.Grid for loadPath in SvgEditor
 */
Ext.define('SvgEditor.property.Grid', {
	extend: 'Ext.grid.property.Grid',
	uses: ['SvgEditor.property.Store',
		'SvgEditor.property.HeaderContainer',
		'Ext.grid.feature.Grouping'],
	alias: 'widget.ux.propertygrid',
	/**
	 * @cfg {String} groupField
	 * The name of the field from the property store to use as the grouping field.
	 */
	groupField: 'group',
	/**
	 * @cfg {String} editableField
	 *
	 */
	editableField: 'editable',
	/**
	 * @cfg {String} groupingConfig
	 *
	 */
	groupingConfig: {},
	viewConfig: {
		forceFit: true,
		getRowClass: function(record)
		{
			return (record.data[this.ownerCt.editableField] == false) ? "x-item-disabled" : "";
		},
		listeners:
				{
					beforeitemmousedown: function(view, record)
					{
						if (record && record.data.disabled)
							return false;
					}
				}
	},
	/**
	 * @private
	 */
	initComponent: function() {
		var me = this, editableField = me.editableField;
		if (!Ext.get('SvgEditor.property.Grid'))
			Ext.getBody().createChild({
				tag: 'style',
				type: 'text/css',
				id: 'SvgEditor.property.Grid',
				html: '.x-item-disabled div.x-grid-cell-inner {color: gray !important;}'
			});
		me.addCls(Ext.baseCSSPrefix + 'property-grid');
		me.plugins = me.plugins || [];
		// Enable cell editing. Inject a custom startEdit which always edits
		// column 1 regardless of which column was clicked.
		me.plugins.push(new Ext.grid.plugin.CellEditing({
			clicksToEdit: me.clicksToEdit,
			// Inject a startEdit which always edits the value column
			startEdit: function(record, column, e)
			{
				if (record.data && record.data[editableField] == false)
					return false;
				// Maintainer: Do not change this 'this' to 'me'! It is the
				// CellEditing object's own scope.
				return this.self.prototype.startEdit.call(this, record, me.headerCt.child('#' + me.valueField));
			}
		}));
		me.features = me.features || [];
		me.groupingFeature = new Ext.grid.feature.Grouping(Ext.apply({
			groupHeaderTpl: '{name}',
			enableGroupingMenu: true,
			groupField: me.groupField
		}, me.groupingConfig));
		me.features.push(me.groupingFeature);
		me.selModel = {
			selType: 'cellmodel',
			onCellSelect: function(position)
			{
				var record = me.store.getAt(position.row);
				if (record && record.data[editableField] == false)
					return false;
				if (position.column != 1)
					position.column = 1;
				return this.self.prototype.onCellSelect.call(this, position);
			}
		};
		me.customRenderers = me.customRenderers || {};
		me.customEditors = me.customEditors || {};
		if (me.store && me.source && me.store.data.items.length == 0)
		{
			me.store.loadRawData(me.source); // harry
		}
		// Create a property.Store from the source object unless configured with
		// a store
		if (!me.store)
		{
			me.propStore = me.store = new SvgEditor.property.Store(me, me.source);
		}
		if (!me.propStore)
			me.propStore = me.store;
		me.columns = new SvgEditor.property.HeaderContainer(me, me.store); // harry
		me.addEvents('beforepropertychange', 'propertychange');
		Ext.grid.Panel.superclass.initComponent.call(me, arguments); // harry
		// Inject a custom implementation of walkCells which only goes up or
		// down
		me.getView().walkCells = this.walkCells;
		// Set up our default editor set for the 4 atomic data types
		me.editors = {
			'date': new Ext.grid.CellEditor({
				field: new Ext.form.field.Date({
					selectOnFocus: true
				})
			}),
			'string': new Ext.grid.CellEditor({
				field: new Ext.form.field.Text({
					selectOnFocus: true
				})
			}),
			'number': new Ext.grid.CellEditor({
				field: new Ext.form.field.Number({
					selectOnFocus: true
				})
			}),
			'boolean': new Ext.grid.CellEditor({
				field: new Ext.form.field.ComboBox({
					editable: false,
					store: [[true, me.headerCt.trueText],
						[false, me.headerCt.falseText]]
				})
			})
		};
		// Track changes to the data so we can fire our events.
		me.store.on('update', me.onUpdate, me);
	},
	/**
	 * returns the correct editor type for the property type, or a custom one
	 * keyed by the property name
	 *
	 * @private
	 */
	getCellEditor: function(record, column)
	{
		var me = this, propName = record.get(me.nameField),
				val = record.get(me.valueField), editor = me.customEditors[propName];
		editor = me.customEditors[record.data.editor] || record.data.gridProperties.editor || me.customEditors[propName]; // harry
		if (editor === undefined) {
			return false;
		}
		// A custom editor was found. If not already wrapped with a CellEditor,
		// wrap it, and stash it back
		// If it's not even a Field, just a config object, instantiate it before
		// wrapping it.
		if (editor)
		{
			if (Ext.isString(editor) && me.editors[editor]) // harry
			{
				editor = me.editors[editor];
			}
			else if (!(editor instanceof Ext.grid.CellEditor))
			{
				if (!(editor instanceof Ext.form.field.Base))
				{
					editor = Ext.ComponentManager.create(editor, 'textfield');
				}
				editor = me.customEditors[propName] = new Ext.grid.CellEditor({
					field: editor
				});
			}
		}
		else if (Ext.isDate(val))
		{
			editor = me.editors.date;
		}
		else if (Ext.isNumber(val))
		{
			editor = me.editors.number;
		}
		else if (Ext.isBoolean(val))
		{
			editor = me.editors['boolean'];
		}
		else
		{
			editor = me.editors.string;
		}
		// Give the editor a unique ID because the CellEditing plugin caches them
		editor.editorId = propName;
		return editor;
	},
	// Custom implementation of walkCells which only goes up and down.
	walkCells: function(pos, direction, e, preventWrap, verifierFn, scope)
	{
		var editableField = this.ownerCt.editableField, f;
		if (direction == 'left')
		{
			direction = 'up';
		}
		else if (direction == 'right')
		{
			direction = 'down';
		}
		// skip diabled records
		if (direction == 'down' && pos.row < pos.record.store.data.length - 1)
		{
			var i = pos.row,
					position = null;

			for (i; i < pos.record.store.data.length - 1; i++)
			{
				f = pos.record.store.getAt(i + 1);

				if (f.data[editableField] == false)
				{
					continue;
				}
				else
				{
					position = i + 1;
					break;
				}
			}
			if (!position)
				return false;
			else if (position > pos.row)
				pos.row = position - 1;
		}
		if (direction == 'up' && pos.row != 0)
		{
			var i = pos.row,
					position = null;

			for (i; i > 0; i--)
			{
				f = pos.record.store.getAt(i - 1);
				if (f.data[editableField] == false)
				{
					continue;
				}
				else
				{
					position = i - 1;
					break;
				}
			}
			if (position == null)
				return false;
			else if (position < pos.row)
				pos.row = position + 1;
		}
		pos = Ext.view.Table.prototype.walkCells.call(this, pos, direction, e, preventWrap, verifierFn, scope);
		if (!pos.column) {
			pos.column = 1;
		}
		return pos;
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.module.ModelTree', {
	extend: 'Ext.tree.Panel',
	rootVisible: false,
	enableDragDrop: true,
	locale: null,
	propertiesPanel: null,
	util: null,
	viewConfig: {
		plugins: {
			ptype: 'treeviewdragdrop',
			ddGroup: 'model-to-canvas',
			enableDrop: false
		}
	},
	collapsible: true,
	initComponent: function() {
		var me = this;
		me.title = me.locale.modelTree.title;
		me.initModel(me);
		me.initTbar(me);
		me.callParent();
	},
	initModel: function(me) {
		me.store = Ext.create('Ext.data.TreeStore', {
			fields: [
				{name: 'id', type: 'string'},
				{name: 'text', type: 'string'},
				{name: 'icon', type: 'string'},
				{name: 'leaf', type: 'bool'},
				{name: 'type', type: 'string'},
				{name: 'view', type: 'string'},
				{name: 'attrs', type: 'object'},
				{name: 'data', type: 'object'},
				{name: 'properties', type: 'object'},
				{name: 'roles', type: 'object'}
			],
			proxy: {
				type: 'ajax',
				url: 'model/Model_' + me.locale.locale + '.json',
				reader: {
					type: 'json'
//					root: 'model'
				}
			},
			listeners: {
				load: {
					fn: function() {
						var attributesJSON = me.util.ajax('Get', 'Properties_' + me.locale.locale + '.json'),
								attributes = Ext.decode(attributesJSON);
						me.propertiesPanel.initAttributes(me.propertiesPanel, attributes);
					}
				}
			}
		});
	},
	initTbar: function(me) {
		me.filterField = Ext.create('Ext.form.field.Text', {
			xtype: 'textfield',
			fieldLabel: me.locale.modelTree.tbar.filterField,
			labelWidth: 40
		});
		me.filterField.on('change', function() {
			me.filterBy(this.getValue(), 'text');
		});
		me.tbar = Ext.create('Ext.form.Panel', {
			layout: 'fit',
			frame: true,
			items: [me.filterField]
		});
	},
	filterBy: function(text, by) {
		this.clearFilter();
		var view = this.getView(),
				me = this,
				nodesAndParents = [];
		// Find the nodes which match the search term, expand them.
		// Then add them and their parents to nodesAndParents.
		this.getRootNode().cascadeBy(function(tree, view) {
			var currNode = this;

			if (currNode && currNode.data[by] && currNode.data[by].toString().toLowerCase().indexOf(text.toLowerCase()) > -1) {
				me.expandPath(currNode.getPath());
				while (currNode.parentNode) {
					nodesAndParents.push(currNode.id);
					currNode = currNode.parentNode;
				}
			}
		}, null, [me, view]);
		// Hide all of the nodes which aren't in nodesAndParents
		this.getRootNode().cascadeBy(function(tree, view) {
			var uiNode = view.getNodeByRecord(this);
			if (uiNode && !Ext.Array.contains(nodesAndParents, this.id)) {
				Ext.get(uiNode).setDisplayed('none');
			}
		}, null, [me, view]);
	},
	clearFilter: function() {
		var view = this.getView();
		this.getRootNode().cascadeBy(function(tree, view) {
			var uiNode = view.getNodeByRecord(this);
			if (uiNode) {
				Ext.get(uiNode).setDisplayed('table-row');
			}
		}, null, [this, view]);
	},
	afterFirstLayout: function() {
		var me = this;
		me.callParent(arguments);
		var canvas = me.up().canvas, body = canvas.body;
		me.cavnasDropTarget = new Ext.dd.DropTarget(body, {
			ddGroup: 'model-to-canvas',
			notifyEnter: function(ddSource, e, data) {
				//Add some flare to invite drop.
				body.stopAnimation();
				body.highlight();
			},
			notifyDrop: function(ddSource, e, data) {
				var nodeConfig = data.records[0].data;
				var xy = e.getXY();
				xy[0] += canvas.body.dom.scrollLeft;
				xy[1] += canvas.body.dom.scrollTop;
				canvas.addModel(canvas, nodeConfig, xy[0], xy[1], true);
				return true;
			}
		});
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.module.PropertiesPanel', {
	extend: 'SvgEditor.property.Grid',
	locale: null,
	stripeRows: true,
	collapsible: true,
	collapsed: true,
	groupField: 'group',
	chilcksToEdit: 1,
	treeStore: null,
	initComponent: function() {
		var me = this;
		me.baseTitle = me.locale.propertiesPanel.title;
		me.tooltipRenderer = function(value, p, record) {
			p.tdAttr = 'title="' + record.data.gridProperties.tooltip + '"';
			return value;
		};
		me.callParent();
		for (var i = 0; i < 2; i++) {
			Ext.apply(me.columns[i], {text: me.locale.propertiesPanel.columns[i]});
		}
	},
	initAttributes: function(propertiesPanel, attributes) {
		if (attributes) {
			propertiesPanel.modelType = 'BPMNDiagram';
			propertiesPanel.attributes = attributes;
		}
		propertiesPanel.setAttributes(propertiesPanel, propertiesPanel.modelType);
	},
	setAttributes: function(propertiesPanel, modelType) {
		if (!modelType) {
			modelType = propertiesPanel.modelType;
		}
		var node = propertiesPanel.treeStore.getNodeById(modelType), data, attributes = [];
		if (node) {
			data = node.data;
			var properties = data.properties;
			Ext.each(properties, function(property) {
				var attrs = propertiesPanel.attributes[property];
				Ext.each(attrs, function(attr) {
					if (attr.visible !== false) {
						if (!attr.gridProperties) {
							attr.gridProperties = {
								editor: 'combobox',
								propId: attr.id,
								tooltip: attr.tip
							};
						}
						attributes.push(attr);
					}
				});
			});
		} else {
			data = {text: propertiesPanel.locale.propertiesPanel.unKnowModel};
		}
		propertiesPanel.setTitle(propertiesPanel.baseTitle + '-' + data.text);
		propertiesPanel.setSource(attributes);
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.module.TopToolbar', {
	extend: 'Ext.toolbar.Toolbar',
	canvas: null,
	initUtil: null,
	locale: null,
	initComponent: function() {
		var me = this;
		me.items = [
			{
				iconCls: 'SaveButton',
				tooltip: me.locale.topToolbar.saveBtn.qtip,
				handler: function() {
					var canvas = me.canvas, diagram = canvas.getDiagram(),
							saveDialog = Ext.create('SvgEditor.module.SaveDialog');
					diagram = {diagram: diagram};
					var diagramJSON = Ext.encode(diagram);
					saveDialog.modelDescribe.setValue(diagramJSON);
					saveDialog.show();
				}
			},
			{
				text: me.locale.topToolbar.openBtn.text,
				iconCls: '',
				tooltip: me.locale.topToolbar.openBtn.qtip,
				handler: function() {
					var canvas = me.canvas;
					canvas.initCanvas(canvas);
					var diagramJSON =
							'{"diagram":[{"modelId":"start","type":"circle","attrs":{"cx":100,"cy":200,"r":20,"fill":"#00ff33","stroke":"#000","fill-opacity":1},"data":{"text":"start"},"text":{"type":"text","attrs":{"x":100,"y":230,"text-anchor":"middle","text":"start","font":"14px \u5b8b\u4f53","stroke":"none","fill":"#000"}},"flowFromId":[],"flowToId":["path-3","path-8","path-9"]},{"modelId":"end","type":"circle","attrs":{"cx":915,"cy":200,"r":20,"fill":"red","stroke":"#000","fill-opacity":1},"data":{"text":"end"},"text":{"type":"text","attrs":{"x":914.9999999999999,"y":230,"text-anchor":"middle","text":"end","font":"14px \u5b8b\u4f53","stroke":"none","fill":"#000"}},"flowFromId":["path-10","path-11","path-12"],"flowToId":[]},{"modelId":"image-2","type":"image","attrs":{"x":423,"y":67,"width":102,"height":82,"src":"model/view/activity/usertask.svg","fill":"white"},"data":{"a":"a1","b":"b2","c":"c3","text":"\u4efb\u52a11"},"text":{"type":"text","attrs":{"x":474,"y":159,"text-anchor":"middle","text":"\u4efb\u52a11","font":"14px \u5b8b\u4f53","stroke":"none","fill":"#000"}},"flowFromId":["path-3"],"flowToId":["path-5","path-12"]},{"modelId":"path-3","type":"path","attrs":{"fill":"none","stroke":"black","path":[["M",121.36314290740077,194.74489532759125],["L",421,121]],"stroke-width":2.5,"arrow-end":"classic-wide-long"},"data":{},"flowFromId":"start","flowToId":"image-2"},{"modelId":"image-4","type":"image","attrs":{"x":421,"y":244,"width":102,"height":82,"src":"model/view/activity/usertask.svg","fill":"white"},"data":{"a":"a1","b":"b2","c":"c3","text":"\u4efb\u52a12"},"text":{"type":"text","attrs":{"x":472,"y":336,"text-anchor":"middle","text":"\u4efb\u52a12","font":"14px \u5b8b\u4f53","stroke":"none","fill":"#000"}},"flowFromId":["path-5","path-8"],"flowToId":["path-7","path-11"]},{"modelId":"path-5","type":"path","attrs":{"fill":"none","stroke":"black","path":[["M",473,151],["L",473,242]],"stroke-width":2.5,"arrow-end":"classic-wide-long"},"data":{},"flowFromId":"image-2","flowToId":"image-4"},{"modelId":"image-6","type":"image","attrs":{"x":421,"y":436,"width":102,"height":82,"src":"model/view/activity/usertask.svg","fill":"white"},"data":{"a":"a1","b":"b2","c":"c3","text":"\u4efb\u52a13"},"text":{"type":"text","attrs":{"x":472,"y":528,"text-anchor":"middle","text":"\u4efb\u52a13","font":"14px \u5b8b\u4f53","stroke":"none","fill":"#000"}},"flowFromId":["path-7","path-9"],"flowToId":["path-10"]},{"modelId":"path-7","type":"path","attrs":{"fill":"none","stroke":"black","path":[["M",472,328],["L",472,434]],"stroke-width":2.5,"arrow-end":"classic-wide-long"},"data":{},"flowFromId":"image-4","flowToId":"image-6"},{"modelId":"path-8","type":"path","attrs":{"fill":"none","stroke":"black","path":[["M",121.44724490829546,204.90058015377718],["L",419,272]],"stroke-width":2.5,"arrow-end":"classic-wide-long"},"data":{},"flowFromId":"start","flowToId":"image-4"},{"modelId":"path-9","type":"path","attrs":{"fill":"none","stroke":"black","path":[["M",117.64543187171391,213.13920599049666],["L",419,437]],"stroke-width":2.5,"arrow-end":"classic-wide-long"},"data":{},"flowFromId":"start","flowToId":"image-6"},{"modelId":"path-10","type":"path","attrs":{"fill":"none","stroke":"black","path":[["M",525,444],["L",896.3464032824519,211.66376137869258]],"stroke-width":2.5,"arrow-end":"classic-wide-long"},"data":{},"flowFromId":"image-6","flowToId":"end"},{"modelId":"path-11","type":"path","attrs":{"fill":"none","stroke":"black","path":[["M",525,275],["L",893.3941206967979,204.1455976089665]],"stroke-width":2.5,"arrow-end":"classic-wide-long"},"data":{},"flowFromId":"image-4","flowToId":"end"},{"modelId":"path-12","type":"path","attrs":{"fill":"none","stroke":"black","path":[["M",527,120],["L",893.4636504104661,195.50715609470043]],"stroke-width":2.5,"arrow-end":"classic-wide-long"},"data":{},"flowFromId":"image-2","flowToId":"end"}]}';
					if (diagramJSON) {
						var diagram = Ext.decode(diagramJSON).diagram;
						canvas.setDiagram(canvas, diagram);
					}
				}
			},
			{
				text: me.locale.topToolbar.resetBtn.text,
				iconCls: '',
				tooltip: me.locale.topToolbar.resetBtn.qtip,
				handler: function() {
					var canvas = me.canvas;
					canvas.initCanvas(canvas);
					if (me.initUtil) {
						me.initUtil.init();
					}
				}
			},
			{
				text: me.locale.topToolbar.clearBtn.text,
				iconCls: '',
				tooltip: me.locale.topToolbar.clearBtn.qtip,
				handler: function() {
					var canvas = me.canvas;
					canvas.initCanvas(canvas);
				}
			}
		];
		me.callParent();
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.module.SaveDialog', {
	extend: 'Ext.window.Window',
	layout: 'fit',
	width: 400,
	height: 300,
	buttonAlign: 'center',
	closable: false,
	initComponent: function() {
		var me = this;
		me.initForm(me);
		me.initBtns(me);
		me.items = [me.form];
		me.callParent();
	},
	initForm: function(me) {
		me.modelName = Ext.create('Ext.form.field.Text',
				{
					xtype: 'textfield',
					name: 'modelName',
					fieldLabel: '模型名称',
					anchor: '100%'
				});
		me.modelDescribe = Ext.create('Ext.form.field.TextArea', {
			name: 'modelDescribe',
			labelAlign: 'top',
			fieldLabel: '模型描述',
			anchor: '100% -50'
		});
		me.form = Ext.create('Ext.form.Panel', {
			fieldDefaults: {
				labelWidth: 60,
				margin: '10 10 0 10'
			},
			items: [me.modelName, me.modelDescribe]
		});
	},
	initBtns: function(me) {
		me.buttons = [
			{
				text: '确认'
			},
			{
				text: '取消',
				handler: function() {
					me.close();
				}
			}
		];
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.module.EditorPanel', {
	extend: 'Ext.panel.Panel',
	layout: 'border',
	readOnly: false,
	locale: null,
	util: null,
	initComponent: function() {
		var me = this;
		me.initModelTree(me);
		me.initPropertiesPanel(me);
		me.initCanvas(me);
		me.initTbar(me);
		me.items = [me.modelTree, me.canvas, me.propertiesPanel];
		me.modelTree.propertiesPanel = me.propertiesPanel;
		me.canvas.treeStore = me.propertiesPanel.treeStore = me.modelTree.store;
		me.callParent();
	},
	initModelTree: function(me) {
		me.modelTree = Ext.create('SvgEditor.module.ModelTree', {
			region: 'west',
			width: 200,
			util: me.util,
			locale: me.locale
		});
	},
	initPropertiesPanel: function(me) {
		me.propertiesPanel = Ext.create('SvgEditor.module.PropertiesPanel', {
			region: 'east',
			width: 300,
			locale: me.locale
		});
	},
	initCanvas: function(me) {
		me.canvas = Ext.create('SvgEditor.module.CanvasPanel', {
			region: 'center',
			readOnly: me.readOnly,
			locale: me.locale,
			util: me.util,
			propertiesPanel: me.propertiesPanel
		});
	},
	initTbar: function(me) {
		me.tbar = me.topBar = Ext.create('SvgEditor.module.TopToolbar', {
			canvas: me.canvas,
			locale: me.locale
		});
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.util.Util', {
	constructor: function(config) {
		var me = this;
		if (config) {
			Ext.apply(me, config);
		}
		me.init(me);
	},
	init: function(me) {

	},
	ajax: function(method, url, config) {
		var returnText = null;
		Ext.Ajax.request({
			url: url,
			async: false,
			method: method,
			params: config == null ? null : config.params,
			jsonData: config == null ? null : config.jsonData,
			form: config == null ? null : config.form,
			success: function(request) {
				returnText = request.responseText;
				if (returnText == null || returnText == "") {
					returnText = true;
				}
			},
			failure: function() {
				returnText = null;
			}
		});
		return returnText;
	},
	minimum: function(number) {
		if (Math.abs(number) < 0.00001) {
			number = 0;
		}
		return number;
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.init.InitWork', {
	init: function(canvas) {
		var startConfig = {
			modelId: 'start',
			type: 'circle',
			attrs: {
				cx: 100, cy: 200, r: 20,
				fill: '#00ff33', 'fill-opacity': 1
			}
		}, endConfig = {
			modelId: 'end',
			type: 'circle',
			attrs: {
				cx: canvas.paper.width - 100, cy: 200, r: 20,
				fill: 'red', 'fill-opacity': 1
			}
		};
		;
		canvas.start = canvas.createModel(canvas, startConfig);
		canvas.end = canvas.createModel(canvas, endConfig);
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.init.Init', {
	mixins: {
		initWork: 'SvgEditor.init.InitWork'
	},
	constructor: function(config) {
		var me = this;
		if (config) {
			Ext.apply(me, config);
		}
	},
	editor: null,
	init: function() {
		var me = this, editor = me.editor, canvas = editor.ep.canvas;
		me.mixins.initWork.init(canvas);
	}
});/**
 *
 * @author 朱雀
 */
Ext.define('SvgEditor.init.Editor', {
	readOnly: false,
	locale: {},
	constructor: function(config) {
		var me = this;
		if (config) {
			Ext.apply(me, config);
		}
		me.init(me);
	},
	init: function(me) {
		me.util = Ext.create('SvgEditor.util.Util');
		me.initLocale(me);
		Ext.QuickTips.init();
		me.ep = Ext.create('SvgEditor.module.EditorPanel', {readOnly: me.readOnly, util: me.util, locale: me.locale});
		var viewport = new Ext.container.Viewport({
			layout: 'fit',
			items: [me.ep]
		});
	},
	initLocale: function(me) {
		me.locale.locale = (navigator.language || navigator.browserLanguage).toLocaleLowerCase();
		var localeJSON = me.util.ajax('Get', 'locale/SvgEditor_' + me.locale.locale + '.json')
		Ext.apply(me.locale, Ext.decode(localeJSON));
	}
});
/**
 *
 * @author 朱雀
 */

Ext.Loader.setPath('SvgEditor', 'Editor');
Ext.require('SvgEditor.init.Editor');
Ext.onReady(function() {
	editor = Ext.create('SvgEditor.init.Editor', {readOnly: false});
	var init = Ext.create('SvgEditor.init.Init', {editor: editor});
	editor.ep.topBar.initUtil = init;
	init.init();
});